Prefetch Abort BehaviorΒΆ

The Prefetch abort has a special twist to it. When a prefetch abort happens the PC has already been changed to the new address.

The abort happens at the new address on the next instruction. That is on the instruction that should have been there, not the instruction that change the PC.

So in the Prefetch Abort handler, the address in the LR is actually +4 from the bad address, not +4 from the original jump instruction.

Here is what the compiler generates for the call to the function pointer in the main function. Notice it stores the PC to LR before it calls the function

From main.c:

       void (*pf)(void);                   /* Define a function pointer */
       pf = (void(*)(void)) 0x10000000;    /* Set it to a bad address */
1ac:   e3a03201    mov r3, #268435456
1b0:   e58d3008    str r3, [sp, #8]
       pf();                               /* Call the function with the pointer */
1b4:   e59d3008    ldr r3, [sp, #8]
1b8:   e1a0e00f    mov lr, pc              /* The PC is saved to LR. LR = PC = 0x1C0 */
1bc:   e1a0f003    mov pc, r3              /* Change PC to 0x10000000. Prefetch abort happens after this. */
       count++;
1c0:   e59d3000  ldr r3, [sp]              /* Increment counter. It lives on the stack */
1c4:   e2833001  add r3, r3, #1
1c8:   e58d3000  str r3, [sp]

At that point it stores the PC, the PC is pointing to 0x1c0. That’s great since that’s where we want to return to.

However the prefetch abort does not occur at 0x1bc. It occurs when it tries to execute the next instruction at the new address of 0x10000000.

Since the PC is already at 0x10000000 when the exception happens, that the address that is copied into the LR. That’s why the LR seems to be wrong. Because it is for the new address, not the old address as we expected.

The original LR is not lost however, it is still there but in the r14 of the Supervisor mode. It is been masked or hidden by the r14 of the abort mode.

To get to the original LR we need to switch modes first. By switching to the Supervisor mode the Supervisor mode’s r14 or LR will be visible again. We can read the value and save it or use it to set the Abort mode’s LR.

After we read the LR from the Supervisor mode, we need to switch back to the Abort mode. Once we are back in Abort mode we can set the Abort mode’s LR to the LR we got from the Supervisor mode.

The rest the exception is handeled the same way as the Data Abort, that is, save the register {r0-r12, LR} to the stack, call abortPrint, then restore {r0-r12, PC} to return back to our main function.

There’s one more detail we need to take care when we switch modes. We need somewhere to save the LR so we can use it when we switch back to the Abort mode. We can’t use the stack since the SP will be the Supervisor mode’s SP. We need to use a register.

But before we can use the register we need to save it’s value first. To do this we save it to the stack before we switch modes. That way it’s saved to the Abort mode’s stack. Then we can switch to the Supervisor mode and back.

After we’re back, we move the value into LR and restore the register’s original value from where we saved it on the stack.

So the whole sequence for the Prefetch Abort handler is:

  • Save R1 to stack
  • Switch to Supervisor mode
  • Move LR to R1
  • Switch to Abort mode
  • Move R1 to LR
  • Restore R1 from stack
  • Save {R0-R12,LR} to stack
  • Set R0 to 1
  • Set R1 to LR
  • Call abortPrint
  • Restore {R0-R12,PC}^ from stack

The last restore with the ^ sends use back to the next line in main where we want to be.

Previous topic

How to Cause Different Types of Exceptions

Next topic

Nesting Interrupts

This Page